/*
 * File:   test_frequency.cpp
 * Author: Pete
 * Test Frequency library example
 * Requires libfrequency.a or .dylib or .dll
 * Ensure you add this to the linker step eg. -L/usr/local/lib -lfrequency
 * or wherever you've compiled the dylib eg. -L./ -lfrequency
 * General Steps for Using Frequency Class:
 *  - Create frequency object
 *  - Set bins on this object
 *  - Add samples to object or SetData with a vector of doubles.
 *  - Dump or report.
 *
 * Example:
 *		Frequency myFreq() = new Frequency();
 *		myFreq->SetBins(0.0, 20.0, 1.0);
 *		std::vector <double> myData;
 *		for (int x=0; x<100; x++) {
 *			int y = (int)(x/20);
 *			myData->insert(y);
 *		}
 *		myFreq->SetData(myData);
 *		myFreq->Dump();
 *		myFreq->Print();
 *
 * If you have boost installed, you can examine a normal distribution
 * frequency distribution
 * Define BOOST in your compile options (-DBOOST)
 *
 * To compile (with boost) do something like below (assuming you've made
 * libfrequency.dylib and it's in this directory.
 *
 *  g++ -Wall -DBOOST -L./ -lfrequency -o test_frequency test_frequency.cpp
 * ./test_frequency
 *
 * Created on April 15, 2013, 3:00 PM
 */

#include <cstdlib>
#include <iostream>
#include <map>
#include <vector>
#include <utility>

#include "frequency.h"

#ifdef BOOST
#include <boost/random.hpp>
#include <boost/random/normal_distribution.hpp>
#endif

using namespace std;

/*
 * Test Frequency class. Has constructor, add and dump methods.
 * Can also get mode, median and average.
 * ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/frequencycpp
 * CND_DISTDIR = dist
 * CND_CONF = Test
 * CND_PLATFORM = GNU-MacOSX
 * Select library config, compile, then select Test, compile and run.
 * DONT UPLOAD.
 */
int main(int argc, char** argv) {


	// Test fProfit
	std::cout << "TESTING Simple Frequency - each sample added individually" << std::endl;
	std::cout << "=========================================================" << std::endl;

	Frequency* myFreq3 = new Frequency();
	myFreq3->SetBins(-22000.0, 200000.0, 1000.0);
	// Display the bins
	myFreq3->DumpBins();
	std::vector<double>* myBinsPtr;
	myBinsPtr = myFreq3->GetBinsPtr();
	std::vector<double> myBins;
	myBins = myFreq3->GetBins();
	delete myBinsPtr;
	myFreq3->AddSample(9337.72);
	myFreq3->AddSample(178460.07);
	myFreq3->AddSample(20087.95);
	myFreq3->AddSample(11854.58);
	myFreq3->AddSample(79604.20);
	myFreq3->AddSample(103418.37);
	myFreq3->AddSample(135694.42);
	myFreq3->AddSample(63511.80);
	myFreq3->AddSample(80725.40);
	myFreq3->AddSample(158619.79);
	myFreq3->Dump();
	std::cout << "There were " << myFreq3->N() << " samples." << std::endl;
	delete myFreq3;


	std::cout << "TEST FREQUENCY CLASS" << std::endl;
	std::cout << "====================" << std::endl << std::endl;
	// Display compile date time
	std::cout << "Program compiled: " << __DATE__ << " " << __TIME__ << std::endl;
	std::cout << " from source file" << __FILE__ << std::endl;
	// Part 1. Test simple sample data with vector of data collected in code.
	// Make a new Frequency
	std::cout << "Example 1. Setting data via C-array" << std::endl;
	std::cout << "===================================" << std::endl;
	std::cout << "Creating myFreq Frequency object..." << std::endl;
	Frequency *myFreq = new Frequency();

	// Set up bins - 0 to 100, step 2
	std::cout << "Setting bins 0 to 100, step 20..." << std::endl;
	myFreq->SetBins(0.0,100.0,20.0);

	// Add sample data to the Frequency object
	// Create an array of numbers (doubles) then load each value into the
	// sample vector.

	// Bin allocation logic
	// Bin 20 takes values 20.00 to <40 ie. from bin=, to bin less than
	std::cout << "Adding sample data to frequency object..." << std::endl;
	double vals[] = {-600.2, 14.0, 28.0, 35.0, 33.6,  99.0, 115.0, 119.00, 350.0, 999.99, 1000.4,};
	double expResults[] = {1, 1, 3, 0, 0, 1, 2, 3};
	for (int i = 0; i < 11 ; i++ ) {
		// Must have run setBins before value is added, because addSample
		// requires bins to allocate to.
		myFreq->AddSample(vals[i]);
	}
	// Test getting a frequency at a particular bin eg. let's look at bin 25
	// which doesn't exist and produces bin not found error.
	std::cout << "Testing bin frequency where bin does not exist..." << std::endl;
	double freqAt25 = myFreq->GetFrequencyAtBin(25);
	std::cout << "Bin at " << freqAt25 << " did not exist" << std::endl;
	// Now let's try a bin that does exist: 20
	double freqAt20 = myFreq->GetFrequencyAtBin(20);
	std::cout << "Testing bin frequency where bin does exist." << std::endl;
	std::cout << "The frequency at bin 20 (20.00-39.999) is " << freqAt20 << std::endl;
	// Get the frequency table an iterate through it
	std::map <double, double> m = myFreq->GetFrequencyTable();
	std::map<double,double>::iterator itM;
	std::cout << "Print map of bins/frequency via iteration and check expected results" << std::endl;
	std::cout << "--------------------------------------------------------------------" << std::endl;
	bool resultOK = true;
	int c= 0;
	for ( itM=m.begin() ; itM != m.end(); itM++ ) {
		//std::cout << (*itM).first << "\t" << (*itM).second <<  std::endl;
		double f = (*itM).second;
		if ( f != expResults[c] ) {
			std::cout << "Error: Expected bin count was " << expResults[c] << " however calculated bin count was " << f << std::endl;
			resultOK = false;
		}
		else {
			std::cout << "Test OK: Expected bin frequency for bin " << (*itM).first << " is " << expResults[c] << " which matches calculated bin frequency " << f << std::endl;
		}
		c++;
	}
	if (resultOK == true ) {
		std::cout << "Test OK - expected results match" << std::endl;
	}
	else {
		std::cout << "Test failed - expected results do not match" << std::endl;
	}


	// Dump report - in tab delim format for further analysis in eg. Excel
	std::cout << "Frequency Test: Dumping object data (bin -tab- frequency)" << std::endl;
	std::cout << "---------------------------------------------------------" << std::endl;
	std::cout << "Note: first and last bins are low-val and hi-val respectively" << std::endl;
	// Take a look at the frequency distribution - in a flat tab delim tree
	// diagram format suitable for importing into things like Excel.
	// Use PrintTree() for a prettier version.
	myFreq->Dump();
	// Number of samples
	std::cout << std::endl;
	std::cout << "Number of samples in myFreq = " << myFreq->N() << std::endl;
	std::cout << "Sum of samples in myFreq = " << myFreq->Sum() << std::endl;
	// Print in tree format
	myFreq->PrintTree();
	// Part 2 - Using boost.
	// To run this section, make sure you -DBOOST in your compiler options.
	// Create a normal distribution, 10000 samples and
	// analyse the Frequency table.
	// Reuse the frequency object by reseting it.
	// Reset Frequency object, and load up a normal distribution sample set.
#ifdef BOOST
	std::cout << "Example 2. Testing boost normal distribution in a Frequency object" << std::endl;
	std::cout << "==================================================================" << std::endl;
	std::vector<double> normalData;
	// We're reusing the myFreq object, so first, clear it.
	myFreq->Reset();
	// How many normal distribution samples do you want to take?
	long numSamples = 100000;
	// Create bins +/- 3 s.d of mean (20), in blocks of 1.0 should contain
	// over 99.97% of samples.
	myFreq->SetBins(8.0,32.0,1.0);

	// Make a normal distribution and fill it with samples,
	// adding to normalData vector as we go.

	boost::mt19937 rng;
	double ndMean = 20.0;
	double ndStdDev = 4.0;
	// Make a normal distribution mean 20.0, std dev 4.0
	boost::normal_distribution<> nd(ndMean, ndStdDev);
	boost::variate_generator<boost::mt19937&,
			boost::normal_distribution<> > var_nor(rng, nd);
	// OK, let's generate the samples
	int i = 0; for (; i < numSamples; ++i)
	{
	    double d = var_nor();
		myFreq->AddSample(d);
	}
	// Let's take a look at this normal distribution
	std::cout << "Printing frequency table for normal distribution mean " << ndMean << ", std dev " << ndStdDev << std::endl;
	std::cout << "---------------------------------------------------------------------" << std::endl;
	myFreq->Dump();
	// Should see a sideways bell curve, mean 20.0, s.d. 4.0
	std::cout << "Printing frequency tree diagram for normal distribution mean" << ndMean << " Std dev " << ndStdDev << std::endl;
	std::cout << "------------------------------------------------------------" << std::endl;
	myFreq->PrintTree();
	std::cout << std::endl << "Basic summary statistics from Normal Distribution" << std::endl;
	std::cout <<              "-------------------------------------------------" << std::endl;
	std::cout << "There are " << myFreq->N() << " samples in myFreq normal distribution" << std::endl;
	std::cout << "Sum of sample data is: " << myFreq->Sum() << std::endl;
	std::cout << "Average is: " << myFreq->Average() << std::endl;
	std::cout << "Std Dev is: " << myFreq->StdDev() << std::endl;
#endif
	std::cout << std::endl << "Example 3: Providing frequency data as a vector<double>" << std::endl;
	std::cout << "=======================================================" << std::endl;
	Frequency* myFreq2 = new Frequency();
 	myFreq2->SetBins(0.0, 20.0, 1.0);
 	std::vector <double> myData;
 	for (int x=0; x<100; x++) {
 		double y = (int)(x/20);
 		myData.push_back(y);
 	}
 	myFreq2->SetData(myData);
 	myFreq2->Dump();
 	myFreq2->PrintTree();
// Finished with our Frequency objects, clean up.
	delete myFreq;
	delete myFreq2;




	/*
	 Adding profit sample:      9337.72
Adding profit sample:      178460.07
Adding profit sample:      20087.95
Adding profit sample:      11854.58
Adding profit sample:      79604.20
Adding profit sample:      103418.37
Adding profit sample:      135694.42
Adding profit sample:      63511.80
Adding profit sample:      80725.40
Adding profit sample:      158619.79
Min sales: 50000
Max sales: 100000
Average sales: 1.3
Samples in sales: 10
Max profit: 4.82412e+228
Min profit: 9337.72
Average profit: 84131.4
StdDev  profit: 60418.9
Sample of data: first 10 observations
*/
	return 0;

}

